home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / ui4 / sendfaxdialog.py < prev    next >
Text File  |  2009-10-09  |  37KB  |  1,034 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Authors: Don Welch
  20. #
  21.  
  22. # StdLib
  23. import operator
  24. import struct
  25. import Queue
  26.  
  27. # Local
  28. from base.g import *
  29. from base import device, utils, pml
  30. from prnt import cups
  31. from base.codes import *
  32. from ui_utils import *
  33.  
  34. # Qt
  35. from PyQt4.QtCore import *
  36. from PyQt4.QtGui import *
  37.  
  38. # Ui
  39. from sendfaxdialog_base import Ui_Dialog
  40. from filetable import FileTable, FILETABLE_TYPE_FAX
  41. from printernamecombobox import PrinterNameComboBox, PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY
  42. from printsettingsdialog import PrintSettingsDialog
  43. from faxsetupdialog import FaxSetupDialog
  44.  
  45.  
  46. PAGE_SELECT_FAX = 0
  47. PAGE_COVERPAGE = 1
  48. PAGE_FILES = 2
  49. PAGE_RECIPIENTS = 3
  50. PAGE_SEND_FAX = 4
  51. PAGE_MAX = 4
  52.  
  53. STATUS_INFORMATION = 0
  54. STATUS_WARNING = 1
  55. STATUS_ERROR = 2
  56.  
  57. MIME_TYPE_COVERPAGE = "application/hplip-fax-coverpage"
  58.  
  59. fax_enabled = prop.fax_build
  60.  
  61. if fax_enabled:
  62.     try:
  63.         from fax import fax
  64.     except ImportError:
  65.         # This can fail on Python < 2.3 due to the datetime module
  66.         # or if fax was diabled during the build
  67.         fax_enabled = False
  68.  
  69. if not fax_enabled:
  70.     log.error("Fax disabled.")
  71.  
  72. coverpages_enabled = False
  73. if fax_enabled:
  74.     try:
  75.         import reportlab
  76.         ver = reportlab.Version
  77.         try:
  78.             ver_f = float(ver)
  79.         except ValueError:
  80.             ver_f = 0.0
  81.  
  82.         if ver_f >= 2.0:
  83.             coverpages_enabled = True
  84.         else:
  85.             log.warn("Pre-2.0 version of Reportlab installed. Fax coverpages disabled.")
  86.  
  87.     except ImportError:
  88.         log.warn("Reportlab not installed. Fax coverpages disabled.")
  89.  
  90. if not coverpages_enabled:
  91.     log.warn("Please install version 2.0+ of Reportlab for coverpage support.")
  92.  
  93. if fax_enabled:
  94.     from fabwindow import FABWindow
  95.  
  96. if coverpages_enabled:
  97.     from fax import coverpages
  98.  
  99.  
  100. class SendFaxDialog(QDialog, Ui_Dialog):
  101.     def __init__(self, parent, printer_name, device_uri=None, args=None):
  102.         QDialog.__init__(self, parent)
  103.         self.setupUi(self)
  104.  
  105.         self.printer_name = printer_name
  106.         if device_uri is not None:
  107.             self.device_uri = device_uri
  108.         else:
  109.             self.device_uri = device.getDeviceURIByPrinterName(self.printer_name)
  110.  
  111.         self.args = args
  112.         self.dev = None
  113.  
  114.         self.dbus_avail, self.service, session_bus = device.init_dbus()
  115.  
  116.         self.CheckTimer = None
  117.         self.lock_file = None
  118.         self.file_list = []
  119.         self.recipient_list = []
  120.  
  121.         self.initUi()
  122.  
  123.         if self.printer_name:
  124.             if coverpages_enabled:
  125.                 QTimer.singleShot(0, self.displayCoverpagePage)
  126.             else:
  127.                 self.lockAndLoad()
  128.                 QTimer.singleShot(0, self.displayFilesPage)
  129.         else:
  130.             QTimer.singleShot(0, self.displaySelectFaxPage)
  131.  
  132.  
  133.     def initUi(self):
  134.         # connect signals/slots
  135.         self.connect(self.CancelButton, SIGNAL("clicked()"), self.CancelButton_clicked)
  136.         self.connect(self.BackButton, SIGNAL("clicked()"), self.BackButton_clicked)
  137.         self.connect(self.NextButton, SIGNAL("clicked()"), self.NextButton_clicked)
  138.  
  139.         self.initSelectFaxPage()
  140.         self.initCoverpagePage()
  141.         self.initFilesPage()
  142.         self.initRecipientsPage()
  143.         self.initSendFaxPage()
  144.  
  145.         # Application icon
  146.         self.setWindowIcon(QIcon(load_pixmap('hp_logo', '128x128')))
  147.  
  148.  
  149.     def lockAndLoad(self):
  150.         # Start up check timer here, since the fax name is now known
  151.         if self.CheckTimer is None:
  152.             self.CheckTimer = QTimer(self)
  153.             self.connect(self.CheckTimer, SIGNAL("timeout()"), self.CheckTimer_timeout)
  154.             self.CheckTimer.start(3000)
  155.  
  156.         # Lock the app
  157.         if self.printer_name and self.lock_file is None:
  158.             ok, self.lock_file = utils.lock_app('hp-sendfax-%s' % self.printer_name, True)
  159.  
  160.             if not ok:
  161.                 log.error("hp-sendfax is already running for fax %s" % self.printer_name)
  162.                 # TODO:
  163.  
  164.     #
  165.     # Select Fax Page
  166.     #
  167.  
  168.     def initSelectFaxPage(self):
  169.         self.FaxComboBox.setType(PRINTERNAMECOMBOBOX_TYPE_FAX_ONLY)
  170.         self.connect(self.FaxComboBox, SIGNAL("PrinterNameComboBox_currentChanged"), self.FaxComboBox_currentChanged)
  171.         self.connect(self.FaxComboBox, SIGNAL("PrinterNameComboBox_noPrinters"), self.FaxComboBox_noPrinters)
  172.         self.connect(self.FaxOptionsButton, SIGNAL("clicked()"), self.FaxOptionsButton_clicked)
  173.         self.connect(self.FaxSetupButton, SIGNAL("clicked()"), self.FaxSetupButton_clicked)
  174.  
  175.         if self.printer_name is not None:
  176.             self.FaxComboBox.setInitialPrinter(self.printer_name)
  177.  
  178.  
  179.     def displaySelectFaxPage(self):
  180.         self.BackButton.setEnabled(False)
  181.         self.updateStepText(PAGE_SELECT_FAX)
  182.  
  183.         if not fax_enabled:
  184.             FailureUI(self, self.__tr("<b>PC send fax support is not enabled.</b><p>Re-install HPLIP with fax support or use the device front panel to send a fax.</p><p>Click <i>OK</i> to exit.</p>"))
  185.             self.close()
  186.             return
  187.  
  188.         if not self.dbus_avail:
  189.             FailureUI(self, self.__tr("<b>PC send fax support requires DBus and hp-systray.</b><p>Please check the HPLIP installation for proper installation of DBus and hp-systray support.</p><p>Click <i>OK</i> to exit.</p>"))
  190.             self.close()
  191.             return
  192.  
  193.         self.FaxComboBox.updateUi()
  194.         self.displayPage(PAGE_SELECT_FAX)
  195.  
  196.  
  197.     def FaxComboBox_currentChanged(self, device_uri, printer_name):
  198.         self.printer_name = printer_name
  199.         self.device_uri = device_uri
  200.  
  201.  
  202.     def FaxComboBox_noPrinters(self):
  203.         FailureUI(self, self
  204.                   .__tr("<b>No installed fax devices found.</b><p>Please setup a fax device and try again (try using 'hp-setup').</p><p>Click <i>OK</i> to exit.</p>"))
  205.         self.close()
  206.  
  207.  
  208.     def FaxOptionsButton_clicked(self):
  209.         dlg = PrintSettingsDialog(self, self.printer_name, fax_mode=True)
  210.         dlg.exec_()
  211.  
  212.  
  213.     def FaxSetupButton_clicked(self):
  214.         dlg = FaxSetupDialog(self, self.device_uri)
  215.         dlg.exec_()
  216.  
  217.     #
  218.     # Coverpage Page
  219.     #
  220.  
  221.     def initCoverpagePage(self):
  222.         self.cover_page_message = ''
  223.         self.cover_page_re = ''
  224.         self.preserve_formatting = False
  225.         self.cover_page_func, cover_page_png = None, None
  226.         self.last_job_id = 0
  227.         self.busy = False
  228.         self.PrevCoverPageButton.setIcon(QIcon(load_pixmap("prev", "16x16")))
  229.         self.NextCoverPageButton.setIcon(QIcon(load_pixmap("next", "16x16")))
  230.  
  231.         if coverpages_enabled:
  232.             self.cover_page_list = coverpages.COVERPAGES.keys()
  233.             self.cover_page_index = self.cover_page_list.index("basic")
  234.             self.cover_page_max = len(self.cover_page_list)-1
  235.             self.cover_page_name = self.cover_page_list[self.cover_page_index]
  236.  
  237.             self.connect(self.PrevCoverPageButton, SIGNAL("clicked()"), self.PrevCoverPageButton_clicked)
  238.             self.connect(self.NextCoverPageButton, SIGNAL("clicked()"), self.NextCoverPageButton_clicked)
  239.             self.connect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
  240.             self.connect(self.MessageEdit, SIGNAL("textChanged()"), self.MessageEdit_textChanged)
  241.             self.connect(self.RegardingEdit, SIGNAL("textChanged(const QString &)"), self.RegardingEdit_textChanged)
  242.             self.connect(self.PreserveFormattingCheckBox, SIGNAL("toggled(bool)"),
  243.                         self.PreserveFormattingCheckBox_toggled)
  244.         else:
  245.             self.CoverPageGroupBox.setEnabled(False)
  246.  
  247.  
  248.     def displayCoverpagePage(self):
  249.         self.BackButton.setEnabled(False) # No going back once printer is chosen
  250.         self.NextButton.setEnabled(True)
  251.  
  252.         self.lockAndLoad()
  253.  
  254.         self.updateCoverpageButtons()
  255.         self.displayCoverpagePreview()
  256.         self.displayPage(PAGE_COVERPAGE)
  257.  
  258.  
  259.     def MessageEdit_textChanged(self):
  260.         self.cover_page_message = unicode(self.MessageEdit.toPlainText())
  261.  
  262.  
  263.     def RegardingEdit_textChanged(self, t):
  264.         self.cover_page_re = unicode(t)
  265.  
  266.  
  267.     def PreserveFormattingCheckBox_toggled(self, b):
  268.         self.preserve_formatting = b
  269.  
  270.  
  271.     def PrevCoverPageButton_clicked(self):
  272.         self.cover_page_index -= 1
  273.         if self.cover_page_index < 0:
  274.             self.cover_page_index = 0
  275.         else:
  276.             self.updateCoverpageButtons()
  277.             self.displayCoverpagePage()
  278.  
  279.  
  280.     def NextCoverPageButton_clicked(self):
  281.         self.cover_page_index += 1
  282.         if self.cover_page_index > self.cover_page_max:
  283.             self.cover_page_index = self.cover_page_max
  284.         else:
  285.             self.updateCoverpageButtons()
  286.             self.displayCoverpagePage()
  287.  
  288.  
  289.     def displayCoverpagePreview(self):
  290.         self.cover_page_name = self.cover_page_list[self.cover_page_index]
  291.         self.cover_page_func = coverpages.COVERPAGES[self.cover_page_name][0]
  292.         self.CoverPageName.setText(QString('<i>"%1"</i>').arg(self.cover_page_name))
  293.         self.CoverPagePreview.setPixmap(load_pixmap(coverpages.COVERPAGES[self.cover_page_name][1], 'other'))
  294.  
  295.         if self.CoverPageGroupBox.isChecked():
  296.             self.addCoverPage()
  297.         else:
  298.             self.removeCoverPage()
  299.  
  300.  
  301.     def updateCoverpageButtons(self):
  302.         enabled = self.CoverPageGroupBox.isChecked()
  303.         self.PrevCoverPageButton.setEnabled(enabled and self.cover_page_index != 0)
  304.         self.NextCoverPageButton.setEnabled(enabled and self.cover_page_index != self.cover_page_max)
  305.  
  306.  
  307.     def CoverPageGroupBox_toggled(self, b):
  308.         self.updateCoverpageButtons()
  309.         if b:
  310.             self.addCoverPage()
  311.         else:
  312.             self.removeCoverPage()
  313.  
  314.  
  315.     def addCoverPage(self):
  316.         self.removeCoverPage()
  317.         self.FilesTable.addFile(self.cover_page_name, MIME_TYPE_COVERPAGE,
  318.                                 self.__tr('HP Fax Coverpage: "%1"').arg(self.cover_page_name),
  319.                                 self.__tr("Cover Page"), 1)
  320.  
  321.  
  322.     def removeCoverPage(self):
  323.         self.FilesTable.removeFileByMIMEType(MIME_TYPE_COVERPAGE)
  324.  
  325.  
  326.     def toggleCoverPage(self, b):
  327.         self.disconnect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
  328.         self.CoverPageGroupBox.setChecked(b)
  329.         self.connect(self.CoverPageGroupBox, SIGNAL("toggled(bool)"), self.CoverPageGroupBox_toggled)
  330.  
  331.  
  332.     #
  333.     # Files Page
  334.     #
  335.  
  336.     def initFilesPage(self):
  337.         self.FilesTable.setType(FILETABLE_TYPE_FAX)
  338.         self.FilesTable.setFaxCallback(self.FileTable_callback)
  339.         self.connect(self.FilesTable, SIGNAL("isEmpty"), self.FilesTable_isEmpty)
  340.         self.connect(self.FilesTable, SIGNAL("isNotEmpty"), self.FilesTable_isNotEmpty)
  341.         self.connect(self.FilesTable, SIGNAL("fileListChanged"), self.FilesTable_fileListChanged)
  342.  
  343.  
  344.     def displayFilesPage(self):
  345.         self.FilesTable.updateUi(False)
  346.  
  347.         if self.args is not None:
  348.             for a in self.args:
  349.                 f = os.path.abspath(os.path.expanduser(a))
  350.                 if os.path.exists(f) and os.access(f, os.R_OK):
  351.                     self.renderFile(f)
  352.  
  353.             self.args = None
  354.  
  355.         self.restoreNextButton()
  356.         self.NextButton.setEnabled(self.FilesTable.isNotEmpty())
  357.         self.BackButton.setEnabled(coverpages_enabled)
  358.         self.FilesPageNote.setText(self.__tr("Note: You may also add files to the fax by printing from any application to the '%1' fax printer.").arg(self.printer_name))
  359.         self.displayPage(PAGE_FILES)
  360.  
  361.  
  362.     def FilesTable_isEmpty(self):
  363.         if self.StackedWidget.currentIndex() == PAGE_FILES:
  364.             self.NextButton.setEnabled(False)
  365.  
  366.  
  367.     def FilesTable_isNotEmpty(self):
  368.         if self.StackedWidget.currentIndex() == PAGE_FILES:
  369.             self.NextButton.setEnabled(True)
  370.  
  371.  
  372.     def FilesTable_fileListChanged(self):
  373.         self.file_list = self.FilesTable.file_list
  374.         self.toggleCoverPage(self.FilesTable.isMIMETypeInList(MIME_TYPE_COVERPAGE))
  375.  
  376.  
  377.     #
  378.     # Recipients Page
  379.     #
  380.  
  381.     def initRecipientsPage(self):
  382.         # setup validators
  383.         self.QuickAddFaxEdit.setValidator(PhoneNumValidator(self.QuickAddFaxEdit))
  384.  
  385.         # Fax address book database
  386.         self.db = fax.FaxAddressBook()
  387.  
  388.         # Fax address book window
  389.         self.fab = FABWindow(self)
  390.         self.fab.setWindowFlags(Qt.Tool) # Keeps the Fab window on top
  391.  
  392.         self.connect(self.fab, SIGNAL("databaseChanged"), self.FABWindow_databaseChanged)
  393.  
  394.         # connect signals
  395.         self.connect(self.QuickAddFaxEdit, SIGNAL("textChanged(const QString &)"),
  396.                     self.QuickAddFaxEdit_textChanged)
  397.         self.connect(self.QuickAddNameEdit, SIGNAL("textChanged(const QString &)"),
  398.                     self.QuickAddNameEdit_textChanged)
  399.         self.connect(self.QuickAddButton, SIGNAL("clicked()"), self.QuickAddButton_clicked)
  400.         self.connect(self.FABButton, SIGNAL("clicked()"), self.FABButton_clicked)
  401.         self.connect(self.AddIndividualButton, SIGNAL("clicked()"), self.AddIndividualButton_clicked)
  402.         self.connect(self.AddGroupButton, SIGNAL("clicked()"), self.AddGroupButton_clicked)
  403.         self.connect(self.RemoveRecipientButton, SIGNAL("clicked()"), self.RemoveRecipientButton_clicked)
  404.         self.connect(self.MoveRecipientUpButton, SIGNAL("clicked()"), self.MoveRecipientUpButton_clicked)
  405.         self.connect(self.MoveRecipientDownButton, SIGNAL("clicked()"), self.MoveRecipientDownButton_clicked)
  406.         self.connect(self.RecipientsTable, SIGNAL("itemSelectionChanged()"),
  407.                     self.RecipientsTable_itemSelectionChanged)
  408.         self.connect(self.RecipientsTable, SIGNAL("itemDoubleClicked(QTableWidgetItem *)"),
  409.                     self.RecipientsTable_itemDoubleClicked)
  410.  
  411.         # setup icons
  412.         self.FABButton.setIcon(QIcon(load_pixmap("fab", "16x16")))
  413.         self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
  414.         self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
  415.         self.RemoveRecipientButton.setIcon(QIcon(load_pixmap("remove_user", "16x16")))
  416.         self.MoveRecipientUpButton.setIcon(QIcon(load_pixmap("up_user", "16x16")))
  417.         self.MoveRecipientDownButton.setIcon(QIcon(load_pixmap("down_user", "16x16")))
  418.         self.QuickAddButton.setIcon(QIcon(load_pixmap("add_user_quick", "16x16")))
  419.  
  420.         # setup initial state
  421.         self.QuickAddButton.setEnabled(False)
  422.  
  423.         self.recipient_headers = [self.__tr("Name"), self.__tr("Fax number"), self.__tr("Notes")]
  424.  
  425.  
  426.     def FABWindow_databaseChanged(self, action, s1='', s2=''):
  427.         self.db.load()
  428.  
  429.         if action in (FAB_NAME_ADD, FAB_GROUP_ADD, FAB_GROUP_RENAME,
  430.                       FAB_GROUP_REMOVE, FAB_GROUP_MEMBERSHIP_CHANGED):
  431.  
  432.             log.debug("Fax address book has changed")
  433.             self.updateAddressBook()
  434.  
  435.         elif action == FAB_NAME_REMOVE:
  436.             log.debug("Fax address book has changed: '%s' removed" % s1)
  437.             all_names = self.db.get_all_names()
  438.             self.recipient_list = filter(lambda x: x in self.recipient_list, all_names)
  439.             self.updateAddressBook()
  440.             self.updateRecipientTable()
  441.  
  442.         elif action == FAB_NAME_RENAME:
  443.             log.debug("Fax address book has changed: '%s' renamed to '%s'" % (s1, s2))
  444.             for i, n in enumerate(self.recipient_list):
  445.                 if n == s1:
  446.                     self.recipient_list[i] = s2
  447.                     self.updateRecipientTable()
  448.                     break
  449.             else:
  450.                 self.updateAddressBook()
  451.  
  452.         elif action == FAB_NAME_DETAILS_CHANGED:
  453.             log.debug("Fax address book has changed: '%s' details changed" % s1)
  454.             self.updateRecipientTable()
  455.  
  456.  
  457.     def displayRecipientsPage(self):
  458.         self.updateAddressBook()
  459.         self.updateRecipientTable()
  460.         self.enableQuickAddButton()
  461.         self.displayPage(PAGE_RECIPIENTS)
  462.         self.restoreNextButton()
  463.         self.BackButton.setEnabled(True)
  464.  
  465.  
  466.     def updateAddressBook(self):
  467.         names = [n for n in self.db.get_all_names() if not n.startswith('__')]
  468.         groups = self.db.get_all_groups()
  469.         self.AddIndividualComboBox.clear()
  470.         self.AddGroupComboBox.clear()
  471.  
  472.         i = 0
  473.         names.sort()
  474.         for n in names:
  475.             if n not in self.recipient_list:
  476.                 data = self.db.get(n)
  477.                 if data['fax']:
  478.                     self.AddIndividualComboBox.addItem(n)
  479.                     i += 1
  480.  
  481.         if i:
  482.             self.AddIndividualButton.setEnabled(True)
  483.             self.AddIndividualComboBox.setEnabled(True)
  484.             #self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user", "16x16")))
  485.  
  486.         else:
  487.             self.AddIndividualButton.setEnabled(False)
  488.             self.AddIndividualComboBox.setEnabled(False)
  489.             #self.AddIndividualButton.setIcon(QIcon(load_pixmap("add_user-disabled", "16x16")))
  490.  
  491.         i = 0
  492.         groups.sort()
  493.         for g in groups:
  494.             for n in self.db.group_members(g):
  495.                 if not n.startswith('__') and n not in self.recipient_list:
  496.                     self.AddGroupComboBox.addItem(g)
  497.                     i += 1
  498.                     break
  499.  
  500.         if i:
  501.             self.AddGroupButton.setEnabled(True)
  502.             self.AddGroupComboBox.setEnabled(True)
  503.             #self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users", "16x16")))
  504.  
  505.         else:
  506.             self.AddGroupButton.setEnabled(False)
  507.             self.AddGroupComboBox.setEnabled(False)
  508.             #self.AddGroupButton.setIcon(QIcon(load_pixmap("add_users-disabled", "16x16")))
  509.  
  510.  
  511.     def updateRecipientTable(self):
  512.         try:
  513.             prev = self.getCurrentRecipient()
  514.         except (TypeError, AttributeError):
  515.             prev = None
  516.  
  517.         self.RecipientsTable.clear()
  518.         self.RecipientsTable.setRowCount(0)
  519.         self.RecipientsTable.setColumnCount(0)
  520.  
  521.         if self.recipient_list:
  522.             num_recipients = len(self.recipient_list)
  523.  
  524.             self.RecipientsTable.setColumnCount(len(self.recipient_headers))
  525.             self.RecipientsTable.setHorizontalHeaderLabels(self.recipient_headers)
  526.             self.RecipientsTable.setRowCount(num_recipients)
  527.             flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  528.  
  529.             j = None
  530.             for row, n in enumerate(self.recipient_list):
  531.                 i = QTableWidgetItem(QString(n))
  532.                 i.setFlags(flags)
  533.                 self.RecipientsTable.setItem(row, 0, i)
  534.                 if prev is not None and n == prev:
  535.                     j = i
  536.  
  537.                 k = self.db.get(n)
  538.                 if not k:
  539.                     continue
  540.  
  541.                 i = QTableWidgetItem(QString(k['fax']))
  542.                 i.setFlags(flags)
  543.                 self.RecipientsTable.setItem(row, 1, i)
  544.  
  545.                 i = QTableWidgetItem(QString(k['notes']))
  546.                 i.setFlags(flags)
  547.                 self.RecipientsTable.setItem(row, 2, i)
  548.  
  549.             self.RecipientsTable.resizeColumnsToContents()
  550.             self.RecipientsTable.resizeRowsToContents()
  551.  
  552.             if j is not None:
  553.                 self.RecipientsTable.setCurrentItem(j)
  554.             else:
  555.                 self.RecipientsTable.setCurrentItem(self.RecipientsTable.item(0, 0))
  556.  
  557.             self.NextButton.setEnabled(True)
  558.  
  559.         else:
  560.             self.enableRecipientListButtons()
  561.             self.NextButton.setEnabled(False)
  562.  
  563.  
  564.     def RecipientsTable_itemSelectionChanged(self):
  565.         current_row = self.RecipientsTable.currentRow()
  566.         num_recipients = len(self.recipient_list)
  567.         self.enableRecipientListButtons(num_recipients > 0,  # remove
  568.                                             num_recipients > 1 and current_row > 0, # up
  569.                                             num_recipients > 1 and current_row < (num_recipients-1)) # down
  570.  
  571.  
  572.     def enableRecipientListButtons(self, enable_remove=False, enable_up_move=False, enable_down_move=False):
  573.         if enable_remove:
  574.             self.RemoveRecipientButton.setEnabled(True)
  575.         else:
  576.             self.RemoveRecipientButton.setEnabled(False)
  577.  
  578.         if enable_up_move:
  579.             self.MoveRecipientUpButton.setEnabled(True)
  580.         else:
  581.             self.MoveRecipientUpButton.setEnabled(False)
  582.  
  583.         if enable_down_move:
  584.             self.MoveRecipientDownButton.setEnabled(True)
  585.         else:
  586.             self.MoveRecipientDownButton.setEnabled(False)
  587.  
  588.  
  589.     def QuickAddFaxEdit_textChanged(self, fax):
  590.         self.enableQuickAddButton(None, unicode(fax))
  591.  
  592.  
  593.     def QuickAddNameEdit_textChanged(self, name):
  594.         self.enableQuickAddButton(unicode(name))
  595.  
  596.  
  597.     def enableQuickAddButton(self, name=None, fax=None):
  598.         if name is None:
  599.             name = unicode(self.QuickAddNameEdit.text())
  600.         if fax is None:
  601.             fax = unicode(self.QuickAddFaxEdit.text())
  602.  
  603.         existing_name = False
  604.         if name:
  605.             existing_name = name in self.db.get_all_names()
  606.  
  607.         if existing_name:
  608.             try:
  609.                 self.QuickAddNameEdit.setStyleSheet("background-color: yellow")
  610.             except AttributeError:
  611.                 pass
  612.         else:
  613.             try:
  614.                 self.QuickAddNameEdit.setStyleSheet("")
  615.             except AttributeError:
  616.                 pass
  617.  
  618.         if name and not existing_name and fax:
  619.             self.QuickAddButton.setEnabled(True)
  620.         else:
  621.             self.QuickAddButton.setEnabled(False)
  622.  
  623.  
  624.     def QuickAddButton_clicked(self):
  625.         name = unicode(self.QuickAddNameEdit.text())
  626.         fax = unicode(self.QuickAddFaxEdit.text())
  627.         self.fab.addName(name, fax)
  628.         self.addRecipient(name)
  629.         self.updateRecipientTable()
  630.         self.QuickAddNameEdit.clear()
  631.         self.QuickAddFaxEdit.clear()
  632.         self.enableQuickAddButton('', '')
  633.  
  634.  
  635.     def AddIndividualButton_clicked(self):
  636.         self.addRecipient(unicode(self.AddIndividualComboBox.currentText()))
  637.  
  638.  
  639.     def AddGroupButton_clicked(self):
  640.         self.addGroup(unicode(self.AddGroupComboBox.currentText()))
  641.  
  642.  
  643.     def RemoveRecipientButton_clicked(self):
  644.         name = self.getCurrentRecipient()
  645.         temp = self.recipient_list[:]
  646.         for i, n in enumerate(temp):
  647.             if name == n:
  648.                 del self.recipient_list[i]
  649.                 self.updateRecipientTable()
  650.                 self.updateAddressBook()
  651.                 break
  652.  
  653.  
  654.     def MoveRecipientUpButton_clicked(self):
  655.         utils.list_move_up(self.recipient_list, self.getCurrentRecipient())
  656.         self.updateRecipientTable()
  657.  
  658.  
  659.     def MoveRecipientDownButton_clicked(self):
  660.         utils.list_move_down(self.recipient_list, self.getCurrentRecipient())
  661.         self.updateRecipientTable()
  662.  
  663.  
  664.     def getCurrentRecipient(self):
  665.         item = self.RecipientsTable.item(self.RecipientsTable.currentRow(), 0)
  666.         if item is not None:
  667.             return unicode(item.text())
  668.         else:
  669.             return u''
  670.  
  671.  
  672.     def addRecipient(self, name, update=True):
  673.         if name not in self.recipient_list and not name.startswith('__'):
  674.             self.recipient_list.append(name)
  675.             if update:
  676.                 self.updateRecipientTable()
  677.                 self.updateAddressBook()
  678.  
  679.  
  680.     def addGroup(self, group):
  681.         for n in self.db.group_members(group):
  682.             self.addRecipient(n, False)
  683.  
  684.         self.updateRecipientTable()
  685.         self.updateAddressBook()
  686.  
  687.  
  688.     def FABButton_clicked(self):
  689.         self.fab.show()
  690.  
  691.  
  692.     def RecipientsTable_itemDoubleClicked(self, item):
  693.         if item is not None:
  694.             row, col = item.row(), item.column()
  695.             if col != 0:
  696.                 item = self.RecipientsTable.item(row, 0)
  697.  
  698.             self.fab.selectByName(unicode(item.text()))
  699.             self.fab.show()
  700.  
  701.  
  702.     #
  703.     # Send Fax Page
  704.     #
  705.  
  706.     def initSendFaxPage(self):
  707.         self.info_icon = QIcon(load_pixmap("info", "16x16"))
  708.         self.warn_icon = QIcon(load_pixmap("warning", "16x16"))
  709.         self.error_icon = QIcon(load_pixmap("error", "16x16"))
  710.         self.busy_icon = QIcon(load_pixmap("busy", "16x16"))
  711.         self.update_queue = Queue.Queue() # UI updates from send thread
  712.         self.event_queue = Queue.Queue() # UI events (cancel) to send thread
  713.         self.send_fax_active = False
  714.  
  715.  
  716.     def displaySendFaxPage(self):
  717.         self.displayPage(PAGE_SEND_FAX)
  718.         self.addStatusMessage(self.__tr("Ready to send fax."), self.info_icon)
  719.         self.NextButton.setText(self.__tr("Send Fax"))
  720.  
  721.  
  722.  
  723.     #
  724.     # Fax
  725.     #
  726.  
  727.     def executeSendFax(self):
  728.         self.NextButton.setEnabled(False)
  729.         self.BackButton.setEnabled(False)
  730.         self.CheckTimer.stop()
  731.         self.busy = True
  732.         phone_num_list = []
  733.  
  734.         ppd_file = cups.getPPD(self.printer_name)
  735.  
  736.         if ppd_file is not None and os.path.exists(ppd_file):
  737.             if file(ppd_file, 'r').read().find('HP Fax') == -1:
  738.                 FailureUI(self, self.__tr("<b>Fax configuration error.</b><p>The CUPS fax queue for '%1' is incorrectly configured.<p>Please make sure that the CUPS fax queue is configured with the 'HPLIP Fax' Model/Driver.").arg(self.printer_name))
  739.                 self.close()
  740.                 return
  741.  
  742.         beginWaitCursor()
  743.  
  744.         mq = device.queryModelByURI(self.device_uri)
  745.  
  746.         self.dev = fax.getFaxDevice(self.device_uri,
  747.                                    self.printer_name, None,
  748.                                    mq['fax-type'])
  749.  
  750.         try:
  751.             try:
  752.                 self.dev.open()
  753.             except Error, e:
  754.                 log.warn(e.msg)
  755.  
  756.             try:
  757.                 self.dev.queryDevice(quick=True)
  758.             except Error, e:
  759.                 log.error("Query device error (%s)." % e.msg)
  760.                 self.dev.error_state = ERROR_STATE_ERROR
  761.  
  762.         finally:
  763.             self.dev.close()
  764.             endWaitCursor()
  765.  
  766.         if self.dev.error_state > ERROR_STATE_MAX_OK and \
  767.             self.dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
  768.  
  769.             FailureUI(self, self.__tr("<b>Device is busy or in an error state (code=%1)</b><p>Please wait for the device to become idle or clear the error and try again.").arg(self.dev.status_code))
  770.             self.NextButton.setEnabled(True)
  771.             return
  772.  
  773.         # Check to make sure queue in CUPS is idle
  774.         self.cups_printers = cups.getPrinters()
  775.         for p in self.cups_printers:
  776.             if p.name == self.printer_name:
  777.                 if p.state == cups.IPP_PRINTER_STATE_STOPPED:
  778.                     FailureUI(self, self.__tr("<b>The CUPS queue for '%1' is in a stopped or busy state.</b><p>Please check the queue and try again.").arg(self.printer_name))
  779.                     self.NextButton.setEnabled(False)
  780.                     return
  781.                 break
  782.  
  783.         log.debug("Recipient list:")
  784.  
  785.         for p in self.recipient_list:
  786.             entry = self.db.get(p)
  787.             phone_num_list.append(entry)
  788.             log.debug("Name=%s Number=%s" % (entry["name"], entry["fax"]))
  789.  
  790.         log.debug("File list:")
  791.  
  792.         for f in self.file_list:
  793.             log.debug(f)
  794.  
  795.         self.dev.sendEvent(EVENT_START_FAX_JOB, self.printer_name, 0, '')
  796.  
  797.         if not self.dev.sendFaxes(phone_num_list, self.file_list, self.cover_page_message,
  798.                                   self.cover_page_re, self.cover_page_func, self.preserve_formatting,
  799.                                   self.printer_name, self.update_queue, self.event_queue):
  800.  
  801.             FailureUI(self, self.__tr("<b>Send fax is active.</b><p>Please wait for operation to complete."))
  802.             self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
  803.             self.busy = False
  804.             self.send_fax_active = False
  805.             #self.NextButton.setEnabled(False)
  806.             self.setCancelCloseButton()
  807.             return
  808.  
  809.         self.send_fax_active = True
  810.         self.setCancelCloseButton()
  811.         self.SendFaxTimer = QTimer(self)
  812.         self.connect(self.SendFaxTimer, SIGNAL('timeout()'), self.SendFaxTimer_timeout)
  813.         self.SendFaxTimer.start(1000) # 1 sec UI updates
  814.  
  815.  
  816.     def setCancelCloseButton(self):
  817.         if self.send_fax_active:
  818.             self.CancelButton.setText(self.__tr("Cancel Send"))
  819.         else:
  820.             self.CancelButton.setText(self.__tr("Close"))
  821.  
  822.  
  823.     def CancelButton_clicked(self):
  824.         if self.send_fax_active:
  825.             self.addStatusMessage(self.__tr("Cancelling job..."), self.warn_icon)
  826.             self.event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
  827.             self.dev.sendEvent(EVENT_FAX_JOB_CANCELED, self.printer_name, 0, '')
  828.         else:
  829.             self.close()
  830.  
  831.  
  832.     def SendFaxTimer_timeout(self):
  833.         while self.update_queue.qsize():
  834.             try:
  835.                 status, page_num, arg = self.update_queue.get(0)
  836.             except Queue.Empty:
  837.                 break
  838.  
  839.             if status == fax.STATUS_IDLE:
  840.                 self.busy = False
  841.                 self.send_fax_active = False
  842.                 self.setCancelCloseButton()
  843.                 self.SendFaxTimer.stop()
  844.  
  845.             elif status == fax.STATUS_PROCESSING_FILES:
  846.                 self.addStatusMessage(self.__tr("Processing page %1...").arg(page_num), self.busy_icon)
  847.  
  848.             elif status == fax.STATUS_SENDING_TO_RECIPIENT:
  849.                 self.addStatusMessage(self.__tr("Sending fax to %1...").arg(arg), self.busy_icon)
  850.  
  851.             elif status == fax.STATUS_DIALING:
  852.                 self.addStatusMessage(self.__tr("Dialing %1...").arg(arg), self.busy_icon)
  853.  
  854.             elif status == fax.STATUS_CONNECTING:
  855.                 self.addStatusMessage(self.__tr("Connecting to %1...").arg(arg), self.busy_icon)
  856.  
  857.             elif status == fax.STATUS_SENDING:
  858.                 self.addStatusMessage(self.__tr("Sending page %1 to %2...").arg(page_num).arg(arg),
  859.                                       self.busy_icon)
  860.  
  861.             elif status == fax.STATUS_CLEANUP:
  862.                 self.addStatusMessage(self.__tr("Cleaning up..."), self.busy_icon)
  863.  
  864.             elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED):
  865.                 self.busy = False
  866.                 self.send_fax_active = False
  867.                 self.setCancelCloseButton()
  868.                 self.SendFaxTimer.stop()
  869.  
  870.                 if status == fax.STATUS_ERROR:
  871.                     result_code, error_state = self.dev.getPML(pml.OID_FAX_DOWNLOAD_ERROR)
  872.                     #FailureUI(self, self.__tr("<b>Fax send error (%s).</b><p>" % pml.DN_ERROR_STR.get(error_state, "Unknown error")))
  873.                     if error_state == pml.DN_ERROR_NONE:
  874.                         self.addStatusMessage(self.__tr("Fax send error (Possible cause: No answer or dialtone)"), self.error_icon)
  875.                     else:
  876.                         self.addStatusMessage(self.__tr("Fax send error (%1)").arg(pml.DN_ERROR_STR.get(error_state, "Unknown error")), self.error_icon)
  877.                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
  878.  
  879.                 elif status == fax.STATUS_BUSY:
  880.                     #FailureUI(self, self.__tr("<b>Fax device is busy.</b><p>Please try again later."))
  881.                     self.addStatusMessage(self.__tr("Fax is busy."), self.error_icon)
  882.                     self.dev.sendEvent(EVENT_FAX_JOB_FAIL, self.printer_name, 0, '')
  883.  
  884.                 elif status == fax.STATUS_COMPLETED:
  885.                     self.addStatusMessage(self.__tr("Send fax job complete."), self.info_icon)
  886.  
  887.                     self.dev.sendEvent(EVENT_END_FAX_JOB, self.printer_name, 0, '')
  888.  
  889.  
  890.     def addStatusMessage(self, text, icon):
  891.         log.debug(text)
  892.         #self.StatusList.addItem(QListWidgetItem(icon, text, self.StatusList))
  893.         QListWidgetItem(icon, text, self.StatusList)
  894.  
  895.     #
  896.     # CheckTimer and Fax Rendering
  897.     #
  898.  
  899.     def FileTable_callback(self, f):
  900.         # Called by FileTable when user adds a file using "Add file..."
  901.         log.debug("FileTable_callback(%s)" % f)
  902.         self.renderFile(f)
  903.  
  904.  
  905.     def renderFile(self, f):
  906.         self.busy = True
  907.         beginWaitCursor()
  908.         try:
  909.             self.last_job_id = cups.printFile(self.printer_name, f, os.path.basename(f))
  910.         finally:
  911.             self.busy = False
  912.             endWaitCursor()
  913.  
  914.  
  915.     def CheckTimer_timeout(self):
  916.         if not self.busy:
  917.             #log.debug("Checking for incoming faxes...")
  918.             device_uri, printer_name, event_code, username, job_id, title, timedate, fax_file = \
  919.                 self.service.CheckForWaitingFax(self.device_uri, prop.username, self.last_job_id)
  920.  
  921.             if fax_file:
  922.                 self.last_job_id = 0
  923.                 log.debug("A new fax has arrived: %s (%d)" % (fax_file, job_id))
  924.                 self.addFileFromJob(fax_file, title)
  925.  
  926.  
  927.     def addFileFromJob(self, fax_file, title):
  928.         self.busy = True
  929.         #beginWaitCursor()
  930.         try:
  931.             ok, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding = \
  932.                 self.getFileInfo(fax_file)
  933.             if ok:
  934.                 self.FilesTable.addFile(fax_file, 'application/hplip-fax', 'HPLIP Fax', title, num_pages)
  935.  
  936.         finally:
  937.             self.busy = False
  938.             endWaitCursor()
  939.  
  940.  
  941.     def getFileInfo(self, fax_file):
  942.         f = file(fax_file, 'r')
  943.         header = f.read(fax.FILE_HEADER_SIZE)
  944.         f.close()
  945.  
  946.         if len(header) != fax.FILE_HEADER_SIZE:
  947.             log.error("Invalid fax file! (truncated header or no data)")
  948.             return (False, 0, 0, 0, 0, 0, 0)
  949.  
  950.         mg, version, num_pages, hort_dpi, vert_dpi, page_size, \
  951.             resolution, encoding, reserved1, reserved2 = \
  952.             struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
  953.  
  954.         log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  955.                   (mg, version, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
  956.  
  957.         return (True, num_pages, hort_dpi, vert_dpi, page_size, resolution, encoding)
  958.  
  959.  
  960.     #
  961.     # Misc
  962.     #
  963.  
  964.     def closeEvent(self, e):
  965.         if self.lock_file is not None:
  966.             utils.unlock(self.lock_file)
  967.         e.accept()
  968.  
  969.  
  970.     def displayPage(self, page):
  971.         self.updateStepText(page)
  972.         self.StackedWidget.setCurrentIndex(page)
  973.  
  974.  
  975. #    def CancelButton_clicked(self):
  976. #        self.close()
  977.  
  978.  
  979.     def BackButton_clicked(self):
  980.         p = self.StackedWidget.currentIndex()
  981.         if p == PAGE_SELECT_FAX:
  982.             log.error("Invalid!")
  983.  
  984.         elif p == PAGE_COVERPAGE:
  985.             log.error("Invalid!")
  986.  
  987.         elif p == PAGE_FILES:
  988.             self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
  989.             self.displayCoverpagePage()
  990.  
  991.         elif p == PAGE_RECIPIENTS:
  992.             self.StackedWidget.setCurrentIndex(PAGE_FILES)
  993.             self.displayFilesPage()
  994.  
  995.         elif p == PAGE_SEND_FAX:
  996.             self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
  997.             self.displayRecipientsPage()
  998.  
  999.  
  1000.     def NextButton_clicked(self):
  1001.         p = self.StackedWidget.currentIndex()
  1002.         if p == PAGE_SELECT_FAX:
  1003.             self.StackedWidget.setCurrentIndex(PAGE_COVERPAGE)
  1004.             self.displayCoverpagePage()
  1005.  
  1006.         elif p == PAGE_COVERPAGE:
  1007.             self.StackedWidget.setCurrentIndex(PAGE_FILES)
  1008.             self.displayFilesPage()
  1009.  
  1010.         elif p == PAGE_FILES:
  1011.             self.StackedWidget.setCurrentIndex(PAGE_RECIPIENTS)
  1012.             self.displayRecipientsPage()
  1013.  
  1014.         elif p == PAGE_RECIPIENTS:
  1015.             self.StackedWidget.setCurrentIndex(PAGE_SEND_FAX)
  1016.             self.displaySendFaxPage()
  1017.  
  1018.         elif p == PAGE_SEND_FAX:
  1019.             self.executeSendFax()
  1020.  
  1021.  
  1022.     def updateStepText(self, p):
  1023.         self.StepText.setText(self.__tr("Step %1 of %2").arg(p+1).arg(PAGE_MAX+1))
  1024.  
  1025.  
  1026.     def restoreNextButton(self):
  1027.         self.NextButton.setText(self.__tr("Next >"))
  1028.  
  1029.  
  1030.     def __tr(self,s,c = None):
  1031.         return qApp.translate("SendFaxDialog",s,c)
  1032.  
  1033.  
  1034.